/*
 * ArchPlatform.cpp
 *
 *  Created on: Jan 13, 2010
 *      Author: artur
 */

#include <x86/ArchPlatform.h>
#include <Console.h>

#define DEBUG
#undef DEBUG

extern kernel::Console cout;

static const trapfn_t intr_table[] =
  { intr_0, intr_1, intr_2, intr_3, intr_4, intr_5, intr_6, intr_7, intr_8,
      intr_9, intr_10, intr_11, intr_12, intr_13, intr_14, intr_15 };

static const trapfn_t trap_table[] =
  { trap_0, trap_1, trap_2, trap_3, trap_4, trap_5, trap_6, trap_7, trap_8,
      trap_9, trap_10, trap_11, trap_12, trap_13, trap_14, trap_15, trap_16,
      trap_17, trap_18 };

#define NTRAPS(x)	(int)(sizeof(x) / sizeof(void *))

namespace kernel
{

  ArchPlatform::ArchPlatform()
  {
    // TODO Auto-generated constructor stub

  }

  ArchPlatform::~ArchPlatform()
  {
    // TODO Auto-generated destructor stub
  }

  void
  ArchPlatform::_archinit(void)
  {
    /* Enable write protection from kernel code */
    set_cr0(get_cr0() | CR0_WP);

    /*
     * TODO : Take sense on flags
     * Setup flag register.
     * Interrupt disable, clear direction, clear nested
     * task, i/o privilege 0
     */
    set_eflags(get_eflags() & ~(EFL_IF | EFL_DF | EFL_NT | EFL_IOPL));

    /*
     * Initialize descriptors.
     * Setup segment and interrupt descriptor.
     */
    __arch_gdt_init();
    __arch_idt_init();

    /* TODO TSS init, also with user segments (NGTS => 6).
     * Status: Fixed */
    __arch_tss_init();
  }

  void *
  ArchPlatform::_get_data_abort_addr(struct cpu_regs *regs)
  {
    return (void *) get_cr2();
  }

  /**
   *  Check MMU flag
   */
  bool
  ArchPlatform::_is_mmu_on(void)
  {
    return true;
  }

  /**
   * Switch mmu
   */
  void
  ArchPlatform::_switch_mmu(void *pgd)
  {
    set_cr3((u32)(pgd));
    return;
  }

  /**
   * Set GDT (global descriptor table) members into specified vector
   */
  void
  ArchPlatform::__arch_gdt_set_gate(int vec, u32 base, size_t limit, u8 access,
      u8 gran)
  {

    /* Setup the descriptor base address */
    gdt[vec].base_low = (base & 0xFFFF);
    gdt[vec].base_mid = (base >> 16) & 0xFF;
    gdt[vec].base_high = (base >> 24) & 0xFF;

    /* Setup the descriptor limits */
    gdt[vec].limit_low = (limit & 0xFFFF);
    gdt[vec].granularity = ((limit >> 16) & 0x0F);

    /* Finally, set up the granularity and access flags */
    gdt[vec].granularity |= (gran & 0xF0);
    gdt[vec].access = access;

  }

  /**
   * Setup the GDT and load it.
   */
  void
  ArchPlatform::__arch_gdt_init(void)
  {

    /* Setup the GDT pointer and limit */
    gp.limit = (sizeof(struct gdt_entry) * NGDTS) - 1;
    gp.base = (u32) & gdt;

    /* Our NULL descriptor */
    __arch_gdt_set_gate(0, 0, 0, 0, 0);

    /*
     * Set system descriptors
     * base = 0
     * limit = 4Gb
     * granularity = 4Kb
     * opcode = 32Bit
     */
#ifdef DEBUG
    ::cout << (char *) "Setting GDT[KERNEL_CS=";
    ::cout << KERNEL_CS;
    ::cout << (char *) "]: gran[";
    ::cout << (ST_PRESENT | ST_KERN | ST_CODE_R);
    ::cout << (char *) "], access[";
    ::cout << (SIZE_4K | SIZE_32);
    ::cout << (char *) "]...";
#endif
    __arch_gdt_set_gate(KERNEL_CS / 8, 0, 0xffffffff, ST_PRESENT | ST_KERN
        | ST_CODE_R, SIZE_4K | SIZE_32);
#ifdef DEBUG
    ::cout << (char *) "OK\n";
#endif

#ifdef DEBUG
    ::cout << "Setting GDT[KERNEL_CS=" << (int) KERNEL_DS << "]: gran["
    << (int) (ST_PRESENT | ST_KERN | ST_DATA_W) << "], access["
    << (int) (SIZE_4K | SIZE_32) << "]...";
#endif
    __arch_gdt_set_gate(KERNEL_DS / 8, 0, 0xffffffff, ST_PRESENT | ST_KERN
        | ST_DATA_W, SIZE_4K | SIZE_32);
#ifdef DEBUG
    ::cout << (char *) "OK\n";
#endif

    /* TODO : FIXME! just only 3 gdt entries this time */
#ifdef DEBUG
    ::cout << "Setting GDT[USER_DS=" << (int) USER_CS << "]: gran["
    << (int) (ST_PRESENT | ST_USER | ST_CODE_R) << "], access["
    << (int) (SIZE_4K | SIZE_32) << "]...";
#endif
    __arch_gdt_set_gate(USER_CS / 8, 0, 0xffffffff, ST_PRESENT | ST_USER
        | ST_CODE_R, SIZE_4K | SIZE_32);
#ifdef DEBUG
    ::cout << (char *) "OK\n";
#endif

#ifdef DEBUG
    ::cout << "Setting GDT[USER_DS=" << (int) USER_DS << "]: gran["
    << (int) (ST_PRESENT | ST_USER | ST_DATA_W) << "], access["
    << (int) (SIZE_4K | SIZE_32) << "]...";
#endif
    __arch_gdt_set_gate(USER_DS / 8, 0, 0xffffffff, ST_PRESENT | ST_USER
        | ST_DATA_W, SIZE_4K | SIZE_32);
#ifdef DEBUG
    ::cout << (char *) "OK\n";
#endif

    /* Clear TSS Busy */
    /* gdt[KERNEL_TSS / 8].type &= ~ST_TSS_BUSY; */

    /* Install new GDT */
    lgdt(&gp);
    ::cout << (char *) "OK\n";
  }

  /**
   * Set IDT (interrupt descriptor table) members into specified vector
   */
  void
  ArchPlatform::__arch_idt_set_gate(int vec, u_long base, u_short sel,
      u_char flags)
  {

    /* The interrupt routine's base address */
    idt[vec].base_lo = (base & 0xFFFF);
    idt[vec].base_hi = (base >> 16) & 0xFFFF;

    /* The segment or 'selector' that this IDT entry will use
     *  is set here, along with any access flags */
    idt[vec].selector = sel;
    idt[vec].always0 = 0;
    idt[vec].flags = flags;
  }

  /**
   * Setup the interrupt descriptor table and load it.
   *
   * IDT layout:
   *  0x00 - 0x12 ... S/W trap
   *  0x13 - 0x1f ... Intel reserved
   *  0x20 - 0x3f ... H/W interrupt
   *  0x40        ... System call trap
   */
  void
  ArchPlatform::__arch_idt_init(void)
  {
    int i;

#if 0
    ::cout << (char *)"Initializing IRQ's...";
    interrupt_init();
    ::cout << (char *)"OK\n";
#endif

    ::cout << (char *) "Lodaing IDT... ";

    idtp.limit = (u16)(sizeof(idt) - 1);
    idtp.base = (u32) & idt;

    /*memset(&idt, 0, sizeof(struct idt_entry) * 256);*/

    /* Fill all vectors with default handler */
    for (i = 0; i < NIDTS; i++)
      {
        __arch_idt_set_gate(i, (u_long) trap_default, KERNEL_CS, ST_PRESENT
            | ST_KERN | ST_TRAP_GATE);
      }

    /* Setup trap handlers */
    for (i = 0; i < NTRAPS(trap_table); i++)
      {
        /* printk ("Setting IDT[%d] 0x%x flags[0x%x]",i,KERNEL_CS, ST_PRESENT | ST_KERN | ST_TRAP_GATE); */
        __arch_idt_set_gate(i, (u_long) trap_table[i], KERNEL_CS, ST_PRESENT
            | ST_KERN | ST_TRAP_GATE);
      }

    /* Setup interrupt handlers */
    for (i = 0; i < 16; i++)
      {
        /*printk ("Setting IDT[%d] 0x%x flags[0x%x]\n",0x20 + i,KERNEL_CS, ST_PRESENT | ST_KERN | ST_INTR_GATE);*/
        __arch_idt_set_gate(0x20 + i, (u_long) intr_table[i], KERNEL_CS,
            ST_PRESENT | ST_KERN | ST_INTR_GATE);
      }

    /* Setup debug trap */
    /*	idt_set_gate(3, trap_3, KERNEL_CS, ST_USER | ST_TRAP_GATE);
     */
    ::cout << (char *) "OK\n";

    /* Setup system call handler */
    ::cout << (char *) "Installing syscall gate... ";

    __arch_idt_set_gate(SYSCALL_INT, (u_long) syscall_entry, KERNEL_CS,
        ST_PRESENT | ST_USER | ST_TRAP_GATE);

    /* Load IDT */
    lidt(&idtp);

    ::cout << (char *) "OK\n";

  }

  /**
   * Set kernel stack pointer in TSS (task state segment).
   * An actual value of the register is automatically set when
   * CPU enters kernel mode next time.
   */
  void
  ArchPlatform::__arch_tss_set(uint32_t kstack)
  {
    root_tss.esp0 = kstack;
  }

  /**
   * tss_get() returns current esp0 value for trap handler.
   */
  uint32_t
  ArchPlatform::__arch_tss_get(void)
  {
    return root_tss.esp0;
  }

  /**
   * TODO: fix changes of gdt_set() and gdt_entry structure
   * Initialize the task state segment.
   * Only one static TSS is used for all contexts.
   */
  void
  ArchPlatform::__arch_tss_init(void)
  {
    __arch_gdt_set_gate(KERNEL_TSS / 8, (u_long) & root_tss, sizeof(struct tss)
        - 1, ST_PRESENT | ST_KERN | ST_TSS, 0);

    /* Setup TSS */
    util::memset(&root_tss, 0, sizeof(struct tss));
    root_tss.ss0 = KERNEL_DS;
    root_tss.esp0 = (uint32_t) phys_to_virt(BOOT_STACK + STACK_SIZE - 1);
    root_tss.cs = (uint32_t) USER_CS | 3;
    root_tss.ds = root_tss.es = root_tss.ss = root_tss.fs = root_tss.gs
        = (uint32_t) USER_CS | 3;
    root_tss.io_bitmap_offset = INVALID_IO_BITMAP;
    ltr( KERNEL_TSS);

    ::cout << "TSS OK\n";

  }

}
